﻿using Sgr.Caching.Services;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace Sgr.Security.Tokens.Services
{
    public class DefaultTemporaryTokenService : ITemporaryTokenService
    {
        private readonly ICacheManager _cacheManager;

        public DefaultTemporaryTokenService(ICacheManager cacheManager)
        {
            _cacheManager = cacheManager;
        }

        /// <summary>
        /// 创建一个新的临时令牌
        /// </summary>
        /// <param name="tokenType">令牌类型</param>
        /// <param name="expirationMinutes">过期时间(分钟) [ 默认5分钟] </param>
        /// <param name="content">令牌内容</param>
        /// <param name="deviceFingerprint">关联的设备指纹 [ 默认无] </param>
        /// <param name="userId">关联的用户标识 [ 默认无] </param>
        /// <param name="cancellationToken"> </param>
        /// <returns></returns>
        public async Task<TemporaryToken> CreateAsync(int tokenType,
            int expirationMinutes = 5,
            string? content = null,
            string? deviceFingerprint = null,
            string? userId = null,
            CancellationToken cancellationToken = default)
        {
            if (expirationMinutes < 1)
                expirationMinutes = 1;

            // 创建令牌
            var token = new TemporaryToken
            {
                Code = Guid.NewGuid().ToString("N"),
                ExpirationTime = DateTime.UtcNow.AddMinutes(expirationMinutes)
            };

            // 创建缓存项内容
            var cacheItem = new CachedTemporaryToken
            {
                Code = token.Code,
                TokenType = tokenType,
                Content = content,
                DeviceFingerprint = deviceFingerprint,
                UserId = userId,
                ExpirationTime = token.ExpirationTime,
                IsUsed = false
            };

            // 设置缓存选项
            var cacheOptions = _cacheManager.CreateCacheEntryOptions()
                .SetAbsoluteExpirationRelativeToNowSecond(expirationMinutes * 60);

            // 将令牌存入缓存
            await _cacheManager.SetAsync(
                getCacheKey(token.Code),
                cacheItem,
                cacheOptions,
                cancellationToken);

            return token;
        }

        /// <summary>
        /// 验证临时令牌
        /// </summary>
        public async Task<bool> ValidateAsync(
            string code,
            int tokenType,
            string? content = null,
            string? deviceFingerprint = null,
            string? userId = null,
            CancellationToken cancellationToken = default)
        {
            // 从缓存获取令牌
            var cacheKey = getCacheKey(code);

            var cachedToken = await _cacheManager.GetAsync<CachedTemporaryToken>(
                cacheKey,
                () => Task.FromResult<CachedTemporaryToken?>(null),
                null,
                cancellationToken);

            //从缓存中删除令牌
            await _cacheManager.RemoveAsync(cacheKey, cancellationToken);

            if (cachedToken == null)
                return false;

            // 验证令牌
            if (cachedToken.IsUsed ||
                DateTime.UtcNow >= cachedToken.ExpirationTime ||
                cachedToken.TokenType != tokenType)
                return false;

            // 验证内容（如果提供）
            if (!string.IsNullOrEmpty(content) && content != cachedToken.Content)
                return false;

            // 验证设备指纹（如果设置）
            if (!string.IsNullOrEmpty(cachedToken.DeviceFingerprint) &&
                cachedToken.DeviceFingerprint != deviceFingerprint)
                return false;

            // 验证用户ID（如果设置）
            if (!string.IsNullOrEmpty(cachedToken.UserId) &&
                cachedToken.UserId != userId)
                return false;

            return true;
        }

        private static string getCacheKey(string code)
        {
            return $"SGR-TemporaryToken-{code}";
        }

        /// <summary>
        /// 缓存中的临时令牌信息
        /// </summary>
        private class CachedTemporaryToken
        {
            public string Code { get; set; } = string.Empty;
            public int TokenType { get; set; }
            public string? Content { get; set; } = string.Empty;
            public string? DeviceFingerprint { get; set; }
            public string? UserId { get; set; }
            public DateTime ExpirationTime { get; set; }
            public bool IsUsed { get; set; }
        }
    }
}